﻿using MathNet.Numerics.Distributions;
using MathNet.Numerics.LinearAlgebra;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Graph.Algorithm.Core.Quant.QuantGeneticAlgm;

namespace Graph.Algorithm.Core.Quant
{
    public class QuantGeneticAlgm
    {

        #region builder

        private static Random randomBuilder { get; set; } = new Random();
        private static VectorBuilder<double> vectorBuilder { get; set; } = Vector<double>.Build;

        //private static MatrixBuilder<double> matBuilder { get; set; } = Matrix<double>.Build;

        #endregion

        #region data

        public static int sequenceLen { get; set; } = 0;

        /// <summary>
        /// 量子群
        /// </summary>
        public static List<Quant[]> sequences { get; set; } = new List<Quant[]>();

        /// <summary>
        /// 目标函数
        /// </summary>
        public static Func<double[], double> targetFunc { get; set; } = (pnt) => { return 0; };

        /// <summary>
        /// 当前最佳量子
        /// </summary>
        public static Quant[] bestSequence { get; set; }

        public static Dictionary<string, double> thetaDict { get; set; } = new Dictionary<string, double>();

        public static Func<int[], double[]> decodeFunc { get; set; } = (binary) => { return new double[1]; };

        #endregion

        public QuantGeneticAlgm(int _sequenceLen)
        {
            //初始化量子自旋角度判断表
            thetaDict = GenThetaDict();
            sequenceLen = _sequenceLen;

            //建立量子群
            for (int i = 0; i < 20; i++)
            {
                List<Quant> sequence = new List<Quant>();
                for (int j = 0; j < sequenceLen; j++)
                {
                    sequence.Add(new Quant() { id = new int[] { i, j } });
                }
                sequences.Add(sequence.ToArray());
            }
        }


        public void Run(int numberOfGeneration)
        {
            while (numberOfGeneration > 0)
            {
                numberOfGeneration--;

#if DEBUG
                //Console.WriteLine("-----------------------------");
                //foreach (var sequence in sequences)
                //{
                //    Console.WriteLine(
                //        string.Join("|", sequence.Select(q => q.alpha)));//                            decodeFunc(sequence.Select(q => q.alpha).ToArray())));
                //}
                //Console.WriteLine("-----------------------------");
#endif 

                Parallel.ForEach(sequences, (sequence) =>
                {
                    sequence.ToList().ForEach(q => q.Collapse());
                });

                //opt
                double minTag = double.MaxValue;
                foreach (var sequence in sequences)
                {
                    var binary = sequence.Select(q => q.collapsedValue).ToArray();

                    //Console.WriteLine(string.Join("|", decodeFunc(binary)));

                    var comment = targetFunc(decodeFunc(binary));
                    if (comment < minTag)
                    {
                        minTag = comment;
                        bestSequence = sequence;
                    }
                }

                //ROTATE

                Parallel.ForEach(sequences, (sequence) =>
                {
                    var thetas = GenTheta(sequence);
                    //Console.WriteLine(string.Join("|", thetas));
                    sequence.ToList().ForEach(q => q.Rotate());
                });

                //foreach (var sequence in sequences)
                //{
                //    var thetas = GenTheta(sequence);
                //    //Console.WriteLine(string.Join("|", thetas));
                //    sequence.ToList().ForEach(q => q.Rotate());
                //}

                //sequences = sequences.OrderBy(s => Math.Abs(Comment(Collapse(s)) - tempTarget)).ToList();
                //bestSequence = sequences.First();

            }
        }

        public static double[] GenTheta(Quant[] currSequence)
        {
            List<double> thetas = new List<double>();

            var currBinary = currSequence.Select(q => q.collapsedValue).ToArray();
            var bestBinary = bestSequence.Select(q => q.collapsedValue).ToArray();

            int fxfb =
                targetFunc(decodeFunc(currBinary)) -
                targetFunc(decodeFunc(bestBinary)) > 0 ? 1 : 0;
            for (int i = 0; i < currSequence.Length; i++)
            {

                var currQ = currSequence[i];
                var bestQ = bestSequence[i];

                int xi = currBinary[i];
                int bi = bestBinary[i];

                if (xi == bi) { thetas.Add(0); currQ.theta = 0; continue; }

                int sign = 0;
                if (currQ.alpha * currQ.beta > 0d)
                    sign = 1;
                else if (currQ.alpha * currQ.beta < 0d)
                    sign = 2;
                else if (currQ.alpha == 0)
                    sign = 3;
                else if (currQ.beta == 0)
                    sign = 4;

                //sign = currQ.alpha * currQ.beta > 0d ? 1 : 0;
                //sign = currQ.alpha * currQ.beta < 0d ? 2 : 0;
                //sign = currQ.alpha == 0 ? 3 : 0;
                //sign = currQ.beta == 0 ? 4 : 0;

                List<double> arrs = new List<double>();
                arrs.Add(xi);
                arrs.Add(bi);
                arrs.Add(fxfb);
                arrs.Add(sign);

                var theta = thetaDict[string.Join("", arrs.ToArray())];
                currQ.theta = theta;

                thetas.Add(theta);

            }
            return thetas.ToArray();
        }



        private static Dictionary<string, double> GenThetaDict()
        {
            var sign = Math.Sign(randomBuilder.NextDouble() - 0.5d);
            Dictionary<string, double> pairs = new Dictionary<string, double>();

            pairs.Add("0000", 0);
            pairs.Add("0010", 0);

            pairs.Add("0101", +1d);
            pairs.Add("0102", -1d);
            pairs.Add("0103", 0d);
            pairs.Add("0104", -1d * sign);
            pairs.Add("0111", -1d);
            pairs.Add("0112", 1d);
            pairs.Add("0113", 1d * sign);
            pairs.Add("0114", 0d);
            pairs.Add("1001", -1d);
            pairs.Add("1002", 1d);
            pairs.Add("1003", 1d * sign);
            pairs.Add("1004", 0d);
            pairs.Add("1011", 1d);
            pairs.Add("1012", -1d);
            pairs.Add("1013", 0d);
            pairs.Add("1014", 1d * sign);

            pairs.Add("1100", 0);
            pairs.Add("1110", 0);

            return pairs;
        }


        public static string GetResult()
        {
            List<int> values = bestSequence.Select(q => q.collapsedValue).ToList();
            List<double> doubles = decodeFunc(values.ToArray()).ToList();
            return string.Join(",", doubles);
        }

      

    }


}
